package com.huahai.security.core.authentication.filter;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.huahai.security.core.authentication.handler.CustomAuthenticationSuccessHandler;
import com.huahai.security.core.properties.SecurityConstants;
import com.huahai.security.core.properties.SecurityProperties;
import com.huahai.security.core.properties.StatusCodeEnum;
import com.huahai.security.core.supports.ResultResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.lang.Nullable;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * @Author: Jun
 * @Date: 2020/7/17
 *
 * JWT 登录认证 过滤器， 继承于UsernamePasswordAuthenticationFilter
 * 用于获取用户登录的信息，创建一个类型为UsernamePasswordAuthenticationToken的token
 * 调用authenticationManager.authenticate()让spring-security去进行验证
 */
public class JwtAuthenticationFilter extends UsernamePasswordAuthenticationFilter {

    private final Logger logger = LoggerFactory.getLogger(getClass());

    private final AuthenticationManager authenticationManager;

    private SecurityProperties securityProperties;

    /**
     * 自定义的JWt登录拦截器 的构造方法
     * @param authenticationManager 认证管理器
     * @param securityProperties    自定义的属性配置类
     * @param customAuthenticationSuccessHandler 自定义的认证成功处理器，登录认证成功之后调用
     */
    public JwtAuthenticationFilter(AuthenticationManager authenticationManager,SecurityProperties securityProperties,
                                   CustomAuthenticationSuccessHandler customAuthenticationSuccessHandler) {
        this.authenticationManager = authenticationManager;
        this.securityProperties = securityProperties;
        setFilterProcessesUrl(SecurityConstants.DEFAULT_LOGIN_PROCESSING_URL_FORM);
        setAuthenticationSuccessHandler(customAuthenticationSuccessHandler);
    }

    /**
     * 重写 UsernamePasswordAuthenticationFilter 的 attemptAuthentication 方法，自定义登录认证流程
     *
     * 注意这里接收到的 request的Content-Type应为application/x-www-from-urlencoded格式，
     * 后台以request.getParameter的方式得到属性值
     * @param request
     * @param response
     * @return
     * @throws AuthenticationException
     */
    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
        logger.info("JwtAuthenticationFilter#attemptAuthentication 开启拦截，拦截URL为： {}", request.getRequestURI());
        if (!"POST".equals(request.getMethod())) {
            throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
        } else {
            String username = obtainUsername(request);
            String password = obtainPassword(request);

            if (username == null) {
                username = "";
            }

            if (password == null) {
                password = "";
            }

            username = username.trim();
            UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);
            return this.authenticationManager.authenticate(authRequest);
        }
    }

    /**
     * 登录认证失败的处理
     * @param request
     * @param response
     * @param failed
     * @throws IOException
     * @throws ServletException
     */
    @Override
    protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException {
        ObjectMapper objectMapper = new ObjectMapper();
        response.setContentType("application/json;charset=UTF-8");
        response.getWriter().write(objectMapper.writeValueAsString(
                ResultResponse.build(StatusCodeEnum.HSC400001.getCode(), StatusCodeEnum.HSC400001.getMessage())
        ));
    }

    @Override
    @Nullable
    protected String obtainPassword(HttpServletRequest request) {
        String passwordParameter = SecurityConstants.DEFAULT_SPRING_SECURITY_FORM_PASSWORD_KEY;
        return request.getParameter(passwordParameter);
    }

    @Override
    @Nullable
    protected String obtainUsername(HttpServletRequest request) {
        String usernameParameter = SecurityConstants.DEFAULT_SPRING_SECURITY_FORM_USERNAME_KEY;
        return request.getParameter(usernameParameter);
    }

    public void setSecurityProperties(SecurityProperties securityProperties) {
        this.securityProperties = securityProperties;
    }
}
